﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "ApiBase.h"
#include "GcmpApiFoundation.h"
#include <cassert>
#include <exception>


namespace gdmp {
    namespace api {
        //class IncompleteType
        //{
        //public:
        //    template <typename T>
        //    static void addRef(void* ptr) { reinterpret_cast<Base*>(ptr)->addRef(); }
        //    template <typename T>
        //    static void unRef(void* ptr) { reinterpret_cast<Base*>(ptr)->unRef(); }
        //};

        class CompleteType
        {
        public:
            template <typename T>
            static void addRef(T* ptr) { ptr->addRef(); }
            template <typename T>
            static void unRef(T* ptr) { ptr->unRef(); }
        };

#pragma region inherit-chain-check
        template <typename Ty1, typename Ty2>
        struct test_inherit_chain_helper {
            static constexpr std::true_type test(const Ty1*);
            static constexpr std::false_type test(...);
            using type = decltype(test((Ty2*)nullptr));
        };

        // 静态检测Ptr转换时模板参数类型是否在同一继承链上
#define CHECK_INHERIT_CHAIN(T,PT,V,VPT) \
        static_assert(!std::is_same<PT,VPT>() || test_inherit_chain_helper<T, V>::type() \
                    || test_inherit_chain_helper<V, T>::type(),"can not convert template type.")
#pragma endregion

        template <class T, class PT = CompleteType>
        class Ptr
        {
        public:
            Ptr() : m_ptr(nullptr) {}
            Ptr(const Ptr& rhs) : m_ptr(rhs.m_ptr)
            {
                if (m_ptr)
                    PT::template addRef<T>(get());
            }
            Ptr(T* ptr, bool attach = true) : m_ptr(ptr)
            {
                if (m_ptr)
                    PT::template addRef<T>(get());
            }

            template <class V, class VPT>
            Ptr(const Ptr<V, VPT>& rhs) : m_ptr(nullptr)
            {
                CHECK_INHERIT_CHAIN(T, PT, V, VPT);
                if (rhs)
                    reset(rhs->template Cast<T>(), false);
            }

            ~Ptr() { reset(nullptr); }

            void operator=(const Ptr<T, PT>& rhs)
            {
                if (&rhs != this)
                    reset(rhs.m_ptr);
            }

            template <class V, class VPT>
            void operator=(const Ptr<V, VPT>& rhs)
            {
                CHECK_INHERIT_CHAIN(T, PT, V, VPT);
                if (rhs)
                    reset(rhs->template Cast<T>(), false);
                else
                    reset(nullptr);
            }

            void reset(T* ptr, bool attach = false)
            {
                if (m_ptr != ptr)
                {
                    if (m_ptr)
                        PT::template unRef<T>(get());
                    m_ptr = ptr;
                    if (!attach && m_ptr)
                        PT::template addRef<T>(get());
                }
            }

            T* operator->() const
            {
                assert(m_ptr != nullptr);
                if (m_ptr == nullptr)
                    throw std::exception();
                return m_ptr;
            }
            T& operator*() const
            {
                return *m_ptr;
            }

            bool operator==(const Ptr& rhs) const { return m_ptr == rhs.m_ptr; }
            bool operator!=(const Ptr& rhs) const { return m_ptr != rhs.m_ptr; }

            operator bool() const { return m_ptr != nullptr; }
            bool isNull() const { return m_ptr == nullptr; }
            bool isNotNull() const { return m_ptr != nullptr; }

            bool operator<(const Ptr& rhs) const { return m_ptr < rhs.m_ptr; }

            template <typename To>
            bool isA() const
            {
                if (!m_ptr)
                    return false;

                return m_ptr->template Cast<To>() != nullptr;
            }
            T* get() const { return m_ptr; }

            // 慎用，分离但是引用计数没有减1，使用时小心，避免泄漏。
            T* detach() { T* t = m_ptr; m_ptr = nullptr; return t; }

        private:
            T* m_ptr;
        };

        template<class To, class From>
        bool isA(const Ptr<From>& from)
        {
            return from.template isA<To>();
        }
    }// namespace gcmp
}// namespace glodon
